require 'rubygems'
require 'metaid'

class XML
  # blank slate
  instance_methods.each { |m| undef_method m unless (m =~ /^__|instance_eval|meta/)}
  
  # Each item in @doc is an array containing three values: type, value, attributes
  
  def initialize(&block)
    @doc = []
    instance_eval(&block)
  end
  
  def method_missing(tag, *args, &block)
    meta_def(tag) do |*args|
      @__to_s = nil # invalidate to_s cache
      if block
        @doc << [:open, tag, args.first]
        instance_eval(&block)
        @doc << [:close, tag]
      else
        value, atts = args.pop, args.pop
        subtags, atts = atts, nil if atts.is_a?(Array)
        if subtags
          @doc << [:open, tag, atts]
          subtags.each {|k| __send__(k, value[k])}
          @doc << [:close, tag]
        else
          @doc << [:open, tag, atts] << [:value, value] << [:close, tag]
        end
      end
    end
    __send__(tag, *args)
  end
  
  def instruct!(atts = nil)
    @doc << [:instruct, atts || {:version => "1.0", :encoding => "UTF-8"}]
  end

  def to_s
    @__to_s ||= @doc.map{|i| _fmt_part i}.join
  end
  
  alias_method :inspect, :to_s
  
  def _fmt_part(part)
    case part[0]
    when :instruct
      "<?xml #{_fmt_atts(part[1])}?>"
    when :open
      part[2] ? "<#{part[1]} #{_fmt_atts(part[2])}>" :
        "<#{part[1]}>"
    when :close
      "</#{part[1]}>"
    when :value
      part[1].to_s
    end
  end

  def _fmt_atts(atts)
    atts.inject([]) {|m, i| m << "#{i[0]}=#{i[1].to_s.inspect}"}.join(' ')
  end
end

def t1(list)
  xml = XML.new do
    instruct!
      reality(:time => Time.now) do
      event do
        type 1
        path "/sharon"
        value "abcdefg"
      end
      list.each {|i| item i}
    end
  end
  xml.to_s
end

puts "**************"

x = 10000

list = (1..x).map {
  {:quality => rand(5), :value => rand(77865)}
}

t1 = Time.now
xml = XML.new do
  states {list.each {|s| state [:quality, :value], s}}
end
t2 = Time.now
s = xml.to_s
t3 = Time.now
e1 = t2 - t1
e2 = t3 - t2
r1 = x / e1
r2 = x / e2
e3 = e1 + e2
r3 = x / e3
puts "build: #{e1} (#{r1}); format: #{e2} (#{r2}); total: #{e3} (#{r3})"

__END__

class EventsController < ServerSide::Controller::Base.mount('/events/:format')
  invoke_by_parameter :format, :default => :html
  
  def before_response
    @events = Events.select
  end
  
  def xml
    render_xml('text/xml+svg') do
      events @events.to_xml
    end
  end
end

xml = XML.new do
  instruct!
  reality(:time => Time.now) do
    event do
      type 1
      path "/sharon"
      value "abcdefg"
    end
    list.each {|i| item i}
  end
end
xml.to_s
